package com.epi.one.main;

import com.epi.one.basic.animal.BlackCat;
import com.epi.one.basic.animal.RedCat;
import com.epi.one.basic.generic.ArrayAlg;
import com.epi.one.basic.generic.MyArray;
import com.epi.one.basic.generic.pair.*;
import com.epi.one.basic.generic.task.MyTask;
import com.epi.one.basic.generic.task.Task;

import java.io.IOException;
import java.time.LocalDate;
import java.util.*;

/**
 * @author dxy
 * @version 1.0
 */
public class GenericDemo {
    public static void main(String[] args)  {
        test();
    }

    public static void test() {
        genericAdvantage();
        genericCompile();
        genericStaticMethod();
        genericStaticExtends();
        genericTypeTransfer();
        genericBridgeMethod();
        printDate();
        myTask();
        standardTask();
    }

    private static void printDate() {
        Date now = new MyDate("Amy");
        Date future = new MyDate("Kai",now.getTime()+3600*24*30*1000L);
        DatePair pair = new DatePair<Date>(now, future);
        DatePair.print(pair,1);
    }

    private static void standardTask() {
        Runnable runnable = Task.asRunnable(() -> {
            Thread.sleep(1000);
            System.out.println("Hello World");
            throw new IOException("Check this out");
        });
        Thread thread = new Thread(runnable);
        thread.start();
    }

    public static void myTask() {
        MyTask task = new MyTask(){
            @Override
            public void task() throws Exception {
                Thread.sleep(1000);
                System.out.println("Hello World");
                throw new IOException("Check this out");
            }
        };
        var v =new Thread(task);
        v.start();
    }


    private static void genericBridgeMethod() {
        LocalDate future = LocalDate.now().plusMonths(1);

        System.out.println("【1】声明类型为 DateInterval: ");   //直接调用，不需要桥方法
        DateInterval d = new DateInterval();
        d.setSecond(future);

        System.out.println("【2】声明类型为 Pair<LocalDate>: "); //父类多态，不需要桥方法
        Pair<LocalDate> l = new DateInterval();
        l.setSecond(future);

        System.out.println("【3】声明类型为 Pair：");           //需要桥方法
        Pair p = new DateInterval();
        p.setSecond(future);

        //DateInterval是Pair的子类，l和p的实例可以是DateInterval
        System.out.println(d instanceof Pair);         // true
        System.out.println(l instanceof DateInterval); // true
        System.out.println(p instanceof DateInterval); // true

    }

    /**
     * MyArray <A> v = new MyArray <B> (E...num);
     * A: 声明类型，也被用于 局部变量的 var 类型推断
     * B: 情况一，B=A且<B>等价与<A>等价于<>；情况二，没有菱形括号B=raw，为原始类型
     * E: MyArray讲 E 推断为 Object，则构造参数必须为 E(Number) 类型
     */
    private static void genericTypeTransfer() {
//        (A=Double, B=raw) 编译期raw可以兼容任何类型A. WARN/运行期可能ClassCastException;
        MyArray<Double> myArray1 = new MyArray(1.3, 2);
//        (A=raw, B=Double) 对B进行类型检查
        MyArray myArray2 = new MyArray<Double>(1.3, 2.0);
//        (A=raw, B=raw) 编译期和运行期不会报错
        MyArray myArray3 = new MyArray(1.3, 2);

        /**
         *  Double是 Number的子类
         *  MyArray<Double>不是 My<Number>的子类
         *  MyArray<Double> 是 My<? extends Number>的子类
         */

//        MyArray<Double> errArray1 = new MyArray<Number>(1.3,2.0);
//        MyArray<Number> errArray2 = new MyArray<Double>(1.3,2.0);
        MyArray<? extends Number> myArray4 = new MyArray<Double>(1.3, 2.0);
        MyArray<? super Double> myArray5 = new MyArray<Number>(1.3, 2);


        var myArray = myArray5;
        var e = myArray.printE();
        var o = myArray.printO();
        System.out.println("============================");
        boolean silent = false;
        Object ei = myArray.getE(1, silent);
        Object oi = myArray.getO(1, silent);
        var ei2 = myArray.getE(1, silent);
    }

    private static void genericStaticExtends() {
        Integer min = ArrayAlg.min(new Integer[]{1, 4, 9, 1, 10});
//        var min2 = ArrayAlg.min(new String[]{"John","KHS","Public"});
        System.out.println(min);
    }

    private static void genericStaticMethod() {
        var s = ArrayAlg.<Object>getMiddle("John", 1, "Public"); // Object 只在编译期做类型检查
        var typeName = s.getClass().getTypeName();
        System.out.println(typeName);  //输出 Integer
    }

    private static void genericCompile() {
        var redCat = new RedCat();
        var blackCat = new BlackCat();
        var pair = new CatPair<>(redCat, blackCat);
    }

    private static void genericAdvantage() {
        var list = new ArrayList<String>();
        list.add("hello");

//        作用1：类型安全检查，避免运行时才抛出异常
//        list.add(123);

//        作用2：避免强制类型转换 (String) 最终字节码里依然是强转，只是代码里不用写
        String s = list.getFirst();     // OK
//        作用3：类型擦除，代码更通用复用，而C++使用模板实例化，编译器为每个具体类型生成一份独立代码
        var list2 = new ArrayList<Integer>();
    }
}
